home *** CD-ROM | disk | FTP | other *** search
/ MacAddict 104 / MacAddict_104_2005-04.iso / Software / Internet & Communication / WordPress 1.2.2 freeware.dmg / wordpress / wp-includes / kses.php < prev    next >
Encoding:
PHP Script  |  2004-05-10  |  19.0 KB  |  568 lines

  1. <?php
  2. // Added wp_ prefix to avoid conflicts with existing kses users
  3. # kses 0.2.1 - HTML/XHTML filter that only allows some elements and attributes
  4. # Copyright (C) 2002, 2003  Ulf Harnhammar
  5. # *** CONTACT INFORMATION ***
  6. #
  7. # E-mail:      metaur at users dot sourceforge dot net
  8. # Web page:    http://sourceforge.net/projects/kses
  9. # Paper mail:  (not at the moment)
  10. #
  11. # [kses strips evil scripts!]
  12. if (!defined('CUSTOM_TAGS'))
  13.     define('CUSTOM_TAGS', false);
  14.     
  15. // You can override this in your my-hacks.php file
  16. if (!CUSTOM_TAGS) {
  17. $allowedtags = array(
  18.                 'a' => array(
  19.                     'href' => array(),
  20.                     'title' => array(),
  21.                     'rel' => array()),
  22.                 'abbr' => array('title' => array()),
  23.                 'acronym' => array('title' => array()),
  24.                 'b' => array(),
  25.                 'blockquote' => array('cite' => array()),
  26. //                'br' => array(),
  27.                 'code' => array(),
  28. //                'del' => array('datetime' => array()),
  29. //                'dd' => array(),
  30. //                'dl' => array(),
  31. //                'dt' => array(),
  32.                 'em' => array(),
  33.                 'i' => array(),
  34. //                'ins' => array('datetime' => array(), 'cite' => array()),
  35. //                'li' => array(),
  36. //                'ol' => array(),
  37. //                'p' => array(),
  38. //                'q' => array(),
  39.                 'strike' => array(),
  40.                 'strong' => array(),
  41. //                'sub' => array(),
  42. //                'sup' => array(),
  43. //                'u' => array(),
  44. //                'ul' => array(),
  45.                 );
  46. }
  47. function wp_kses($string, $allowed_html, $allowed_protocols =
  48.                array('http', 'https', 'ftp', 'news', 'nntp', 'telnet',
  49.                      'gopher', 'mailto'))
  50. ###############################################################################
  51. # This function makes sure that only the allowed HTML element names, attribute
  52. # names and attribute values plus only sane HTML entities will occur in
  53. # $string. You have to remove any slashes from PHP's magic quotes before you
  54. # call this function.
  55. ###############################################################################
  56. {
  57.   $string = wp_kses_no_null($string);
  58.   $string = wp_kses_js_entities($string);
  59.   $string = wp_kses_normalize_entities($string);
  60.   $string = wp_kses_hook($string);
  61.   $allowed_html_fixed = wp_kses_array_lc($allowed_html);
  62.   return wp_kses_split($string, $allowed_html_fixed, $allowed_protocols);
  63. } # function wp_kses
  64.  
  65.  
  66. function wp_kses_hook($string)
  67. ###############################################################################
  68. # You add any kses hooks here.
  69. ###############################################################################
  70. {
  71.   return $string;
  72. } # function wp_kses_hook
  73.  
  74.  
  75. function wp_kses_version()
  76. ###############################################################################
  77. # This function returns kses' version number.
  78. ###############################################################################
  79. {
  80.   return '0.2.1';
  81. } # function wp_kses_version
  82.  
  83.  
  84. function wp_kses_split($string, $allowed_html, $allowed_protocols)
  85. ###############################################################################
  86. # This function searches for HTML tags, no matter how malformed. It also
  87. # matches stray ">" characters.
  88. ###############################################################################
  89. {
  90.   return preg_replace('%(<'.   # EITHER: <
  91.                       '[^>]*'. # things that aren't >
  92.                       '(>|$)'. # > or end of string
  93.                       '|>)%e', # OR: just a >
  94.                       "wp_kses_split2('\\1', \$allowed_html, ".
  95.                       '$allowed_protocols)',
  96.                       $string);
  97. } # function wp_kses_split
  98.  
  99.  
  100. function wp_kses_split2($string, $allowed_html, $allowed_protocols)
  101. ###############################################################################
  102. # This function does a lot of work. It rejects some very malformed things
  103. # like <:::>. It returns an empty string, if the element isn't allowed (look
  104. # ma, no strip_tags()!). Otherwise it splits the tag into an element and an
  105. # attribute list.
  106. ###############################################################################
  107. {
  108.   $string = wp_kses_stripslashes($string);
  109.  
  110.   if (substr($string, 0, 1) != '<')
  111.     return '>';
  112.     # It matched a ">" character
  113.  
  114.   if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
  115.     return '';
  116.     # It's seriously malformed
  117.  
  118.   $slash = trim($matches[1]);
  119.   $elem = $matches[2];
  120.   $attrlist = $matches[3];
  121.  
  122.   if (!is_array($allowed_html[strtolower($elem)]))
  123.     return '';
  124.     # They are using a not allowed HTML element
  125.  
  126.   return wp_kses_attr("$slash$elem", $attrlist, $allowed_html,
  127.                    $allowed_protocols);
  128. } # function wp_kses_split2
  129.  
  130.  
  131. function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols)
  132. ###############################################################################
  133. # This function removes all attributes, if none are allowed for this element.
  134. # If some are allowed it calls wp_kses_hair() to split them further, and then it
  135. # builds up new HTML code from the data that kses_hair() returns. It also
  136. # removes "<" and ">" characters, if there are any left. One more thing it
  137. # does is to check if the tag has a closing XHTML slash, and if it does,
  138. # it puts one in the returned code as well.
  139. ###############################################################################
  140. {
  141. # Is there a closing XHTML slash at the end of the attributes?
  142.  
  143.   $xhtml_slash = '';
  144.   if (preg_match('%\s/\s*$%', $attr))
  145.     $xhtml_slash = ' /';
  146.  
  147. # Are any attributes allowed at all for this element?
  148.  
  149.   if (count($allowed_html[strtolower($element)]) == 0)
  150.     return "<$element$xhtml_slash>";
  151.  
  152. # Split it
  153.  
  154.   $attrarr = wp_kses_hair($attr, $allowed_protocols);
  155.  
  156. # Go through $attrarr, and save the allowed attributes for this element
  157. # in $attr2
  158.  
  159.   $attr2 = '';
  160.  
  161.   foreach ($attrarr as $arreach)
  162.   {
  163.     $current = $allowed_html[strtolower($element)]
  164.                             [strtolower($arreach['name'])];
  165.     if ($current == '')
  166.       continue; # the attribute is not allowed
  167.  
  168.     if (!is_array($current))
  169.       $attr2 .= ' '.$arreach['whole'];
  170.     # there are no checks
  171.  
  172.     else
  173.     {
  174.     # there are some checks
  175.       $ok = true;
  176.       foreach ($current as $currkey => $currval)
  177.         if (!wp_kses_check_attr_val($arreach['value'], $arreach['vless'],
  178.                                  $currkey, $currval))
  179.         { $ok = false; break; }
  180.  
  181.       if ($ok)
  182.         $attr2 .= ' '.$arreach['whole']; # it passed them
  183.     } # if !is_array($current)
  184.   } # foreach
  185.  
  186. # Remove any "<" or ">" characters
  187.  
  188.   $attr2 = preg_replace('/[<>]/', '', $attr2);
  189.  
  190.   return "<$element$attr2$xhtml_slash>";
  191. } # function wp_kses_attr
  192.  
  193.  
  194. function wp_kses_hair($attr, $allowed_protocols)
  195. ###############################################################################
  196. # This function does a lot of work. It parses an attribute list into an array
  197. # with attribute data, and tries to do the right thing even if it gets weird
  198. # input. It will add quotes around attribute values that don't have any quotes
  199. # or apostrophes around them, to make it easier to produce HTML code that will
  200. # conform to W3C's HTML specification. It will also remove bad URL protocols
  201. # from attribute values.
  202. ###############################################################################
  203. {
  204.   $attrarr = array();
  205.   $mode = 0;
  206.   $attrname = '';
  207.  
  208. # Loop through the whole attribute list
  209.  
  210.   while (strlen($attr) != 0)
  211.   {
  212.     $working = 0; # Was the last operation successful?
  213.  
  214.     switch ($mode)
  215.     {
  216.       case 0: # attribute name, href for instance
  217.  
  218.         if (preg_match('/^([-a-zA-Z]+)/', $attr, $match))
  219.         {
  220.           $attrname = $match[1];
  221.           $working = $mode = 1;
  222.           $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
  223.         }
  224.  
  225.         break;
  226.  
  227.       case 1: # equals sign or valueless ("selected")
  228.  
  229.         if (preg_match('/^\s*=\s*/', $attr)) # equals sign
  230.         {
  231.           $working = 1; $mode = 2;
  232.           $attr = preg_replace('/^\s*=\s*/', '', $attr);
  233.           break;
  234.         }
  235.  
  236.         if (preg_match('/^\s+/', $attr)) # valueless
  237.         {
  238.           $working = 1; $mode = 0;
  239.           $attrarr[] = array
  240.                         ('name'  => $attrname,
  241.                          'value' => '',
  242.                          'whole' => $attrname,
  243.                          'vless' => 'y');
  244.           $attr = preg_replace('/^\s+/', '', $attr);
  245.         }
  246.  
  247.         break;
  248.  
  249.       case 2: # attribute value, a URL after href= for instance
  250.  
  251.         if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match))
  252.          # "value"
  253.         {
  254.           $thisval = wp_kses_bad_protocol($match[1], $allowed_protocols);
  255.  
  256.           $attrarr[] = array
  257.                         ('name'  => $attrname,
  258.                          'value' => $thisval,
  259.                          'whole' => "$attrname=\"$thisval\"",
  260.                          'vless' => 'n');
  261.           $working = 1; $mode = 0;
  262.           $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
  263.           break;
  264.         }
  265.  
  266.         if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match))
  267.          # 'value'
  268.         {
  269.           $thisval = wp_kses_bad_protocol($match[1], $allowed_protocols);
  270.  
  271.           $attrarr[] = array
  272.                         ('name'  => $attrname,
  273.                          'value' => $thisval,
  274.                          'whole' => "$attrname='$thisval'",
  275.                          'vless' => 'n');
  276.           $working = 1; $mode = 0;
  277.           $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
  278.           break;
  279.         }
  280.  
  281.         if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match))
  282.          # value
  283.         {
  284.           $thisval = wp_kses_bad_protocol($match[1], $allowed_protocols);
  285.  
  286.           $attrarr[] = array
  287.                         ('name'  => $attrname,
  288.                          'value' => $thisval,
  289.                          'whole' => "$attrname=\"$thisval\"",
  290.                          'vless' => 'n');
  291.                          # We add quotes to conform to W3C's HTML spec.
  292.           $working = 1; $mode = 0;
  293.           $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
  294.         }
  295.  
  296.         break;
  297.     } # switch
  298.  
  299.     if ($working == 0) # not well formed, remove and try again
  300.     {
  301.       $attr = wp_kses_html_error($attr);
  302.       $mode = 0;
  303.     }
  304.   } # while
  305.  
  306.   if ($mode == 1)
  307.   # special case, for when the attribute list ends with a valueless
  308.   # attribute like "selected"
  309.     $attrarr[] = array
  310.                   ('name'  => $attrname,
  311.                    'value' => '',
  312.                    'whole' => $attrname,
  313.                    'vless' => 'y');
  314.  
  315.   return $attrarr;
  316. } # function wp_kses_hair
  317.  
  318.  
  319. function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue)
  320. ###############################################################################
  321. # This function performs different checks for attribute values. The currently
  322. # implemented checks are "maxlen", "minlen", "maxval", "minval" and "valueless"
  323. # with even more checks to come soon.
  324. ###############################################################################
  325. {
  326.   $ok = true;
  327.  
  328.   switch (strtolower($checkname))
  329.   {
  330.     case 'maxlen':
  331.     # The maxlen check makes sure that the attribute value has a length not
  332.     # greater than the given value. This can be used to avoid Buffer Overflows
  333.     # in WWW clients and various Internet servers.
  334.  
  335.       if (strlen($value) > $checkvalue)
  336.         $ok = false;
  337.       break;
  338.  
  339.     case 'minlen':
  340.     # The minlen check makes sure that the attribute value has a length not
  341.     # smaller than the given value.
  342.  
  343.       if (strlen($value) < $checkvalue)
  344.         $ok = false;
  345.       break;
  346.  
  347.     case 'maxval':
  348.     # The maxval check does two things: it checks that the attribute value is
  349.     # an integer from 0 and up, without an excessive amount of zeroes or
  350.     # whitespace (to avoid Buffer Overflows). It also checks that the attribute
  351.     # value is not greater than the given value.
  352.     # This check can be used to avoid Denial of Service attacks.
  353.  
  354.       if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
  355.         $ok = false;
  356.       if ($value > $checkvalue)
  357.         $ok = false;
  358.       break;
  359.  
  360.     case 'minval':
  361.     # The minval check checks that the attribute value is a positive integer,
  362.     # and that it is not smaller than the given value.
  363.  
  364.       if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
  365.         $ok = false;
  366.       if ($value < $checkvalue)
  367.         $ok = false;
  368.       break;
  369.  
  370.     case 'valueless':
  371.     # The valueless check checks if the attribute has a value
  372.     # (like <a href="blah">) or not (<option selected>). If the given value
  373.     # is a "y" or a "Y", the attribute must not have a value.
  374.     # If the given value is an "n" or an "N", the attribute must have one.
  375.  
  376.       if (strtolower($checkvalue) != $vless)
  377.         $ok = false;
  378.       break;
  379.   } # switch
  380.  
  381.   return $ok;
  382. } # function wp_kses_check_attr_val
  383.  
  384.  
  385. function wp_kses_bad_protocol($string, $allowed_protocols)
  386. ###############################################################################
  387. # This function removes all non-allowed protocols from the beginning of
  388. # $string. It ignores whitespace and the case of the letters, and it does
  389. # understand HTML entities. It does its work in a while loop, so it won't be
  390. # fooled by a string like "javascript:javascript:alert(57)".
  391. ###############################################################################
  392. {
  393.   $string = wp_kses_no_null($string);
  394.   $string2 = $string.'a';
  395.  
  396.   while ($string != $string2)
  397.   {
  398.     $string2 = $string;
  399.     $string = wp_kses_bad_protocol_once($string, $allowed_protocols);
  400.   } # while
  401.  
  402.   return $string;
  403. } # function wp_kses_bad_protocol
  404.  
  405.  
  406. function wp_kses_no_null($string)
  407. ###############################################################################
  408. # This function removes any NULL or chr(173) characters in $string.
  409. ###############################################################################
  410. {
  411.   $string = preg_replace('/\0+/', '', $string);
  412.   $string = preg_replace('/(\\\\0)+/', '', $string);
  413.  
  414.   return $string;
  415. } # function wp_kses_no_null
  416.  
  417.  
  418. function wp_kses_stripslashes($string)
  419. ###############################################################################
  420. # This function changes the character sequence  \"  to just  "
  421. # It leaves all other slashes alone. It's really weird, but the quoting from
  422. # preg_replace(//e) seems to require this.
  423. ###############################################################################
  424. {
  425.   return preg_replace('%\\\\"%', '"', $string);
  426. } # function wp_kses_stripslashes
  427.  
  428.  
  429. function wp_kses_array_lc($inarray)
  430. ###############################################################################
  431. # This function goes through an array, and changes the keys to all lower case.
  432. ###############################################################################
  433. {
  434.   $outarray = array();
  435.  
  436.   foreach ($inarray as $inkey => $inval)
  437.   {
  438.     $outkey = strtolower($inkey);
  439.     $outarray[$outkey] = array();
  440.  
  441.     foreach ($inval as $inkey2 => $inval2)
  442.     {
  443.       $outkey2 = strtolower($inkey2);
  444.       $outarray[$outkey][$outkey2] = $inval2;
  445.     } # foreach $inval
  446.   } # foreach $inarray
  447.  
  448.   return $outarray;
  449. } # function wp_kses_array_lc
  450.  
  451.  
  452. function wp_kses_js_entities($string)
  453. ###############################################################################
  454. # This function removes the HTML JavaScript entities found in early versions of
  455. # Netscape 4.
  456. ###############################################################################
  457. {
  458.   return preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
  459. } # function wp_kses_js_entities
  460.  
  461.  
  462. function wp_kses_html_error($string)
  463. ###############################################################################
  464. # This function deals with parsing errors in wp_kses_hair(). The general plan is
  465. # to remove everything to and including some whitespace, but it deals with
  466. # quotes and apostrophes as well.
  467. ###############################################################################
  468. {
  469.   return preg_replace('/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string);
  470. } # function wp_kses_html_error
  471.  
  472.  
  473. function wp_kses_bad_protocol_once($string, $allowed_protocols)
  474. ###############################################################################
  475. # This function searches for URL protocols at the beginning of $string, while
  476. # handling whitespace and HTML entities.
  477. ###############################################################################
  478. {
  479.   return preg_replace('/^((&[^;]*;|[\sA-Za-z0-9])*)'.
  480.                       '(:|:|&#[Xx]3[Aa];)\s*/e',
  481.                       'wp_kses_bad_protocol_once2("\\1", $allowed_protocols)',
  482.                       $string);
  483. } # function wp_kses_bad_protocol_once
  484.  
  485.  
  486. function wp_kses_bad_protocol_once2($string, $allowed_protocols)
  487. ###############################################################################
  488. # This function processes URL protocols, checks to see if they're in the white-
  489. # list or not, and returns different data depending on the answer.
  490. ###############################################################################
  491. {
  492.   $string2 = wp_kses_decode_entities($string);
  493.   $string2 = preg_replace('/\s/', '', $string2);
  494.   $string2 = wp_kses_no_null($string2);
  495.   $string2 = strtolower($string2);
  496.  
  497.   $allowed = false;
  498.   foreach ($allowed_protocols as $one_protocol)
  499.     if (strtolower($one_protocol) == $string2)
  500.     {
  501.       $allowed = true;
  502.       break;
  503.     }
  504.  
  505.   if ($allowed)
  506.     return "$string2:";
  507.   else
  508.     return '';
  509. } # function wp_kses_bad_protocol_once2
  510.  
  511.  
  512. function wp_kses_normalize_entities($string)
  513. ###############################################################################
  514. # This function normalizes HTML entities. It will convert "AT&T" to the correct
  515. # "AT&T", ":" to ":", "&#XYZZY;" to "&#XYZZY;" and so on.
  516. ###############################################################################
  517. {
  518. # Disarm all entities by converting & to &
  519.  
  520.   $string = str_replace('&', '&', $string);
  521.  
  522. # Change back the allowed entities in our entity whitelist
  523.  
  524.   $string = preg_replace('/&([A-Za-z][A-Za-z0-9]{0,19});/',
  525.                          '&\\1;', $string);
  526.   $string = preg_replace('/&#0*([0-9]{1,5});/e',
  527.                          'wp_kses_normalize_entities2("\\1")', $string);
  528.   $string = preg_replace('/&#([Xx])0*(([0-9A-Fa-f]{2}){1,2});/',
  529.                          '&#\\1\\2;', $string);
  530.  
  531.   return $string;
  532. } # function wp_kses_normalize_entities
  533.  
  534.  
  535. function wp_kses_normalize_entities2($i)
  536. ###############################################################################
  537. # This function helps wp_kses_normalize_entities() to only accept 16 bit values
  538. # and nothing more for &#number; entities.
  539. ###############################################################################
  540. {
  541.   return (($i > 65535) ? "&#$i;" : "&#$i;");
  542. } # function wp_kses_normalize_entities2
  543.  
  544.  
  545. function wp_kses_decode_entities($string)
  546. ###############################################################################
  547. # This function decodes numeric HTML entities (A and A). It doesn't
  548. # do anything with other entities like ä, but we don't need them in the
  549. # URL protocol whitelisting system anyway.
  550. ###############################################################################
  551. {
  552.   $string = preg_replace('/&#([0-9]+);/e', 'chr("\\1")', $string);
  553.   $string = preg_replace('/&#[Xx]([0-9A-Fa-f]+);/e', 'chr(hexdec("\\1"))',
  554.                          $string);
  555.  
  556.   return $string;
  557. } # function wp_kses_decode_entities
  558.  
  559. function wp_filter_kses($data) {
  560.     global $allowedtags;
  561.     return wp_kses($data, $allowedtags);
  562. }
  563.  
  564. // Filter untrusted content
  565. add_filter('comment_author', 'wp_filter_kses');
  566. add_filter('comment_text', 'wp_filter_kses');
  567.  
  568. ?>